# coding: utf-8

import os
import json
import urllib
import random
import time

from datetime import date, timedelta
from sqlalchemy import or_

from common.constant import MULTI_SMS_QUEUE
from common.producer import Producer
from common.utils import DB
from common.models import model_factory, DemandCate
from common.consumer import ConsumerBase
from common.log import Log


class MarketingSmsConsumer(ConsumerBase):
    """
    create by ld_wu 2018-04-24
    拉新新订单短信消费者
    """

    @staticmethod
    def msg_callback(channel, method, properties, body):
        params = json.loads(body)
        try:
            getattr(MarketingSmsProcess(), params['func'])(
                *params['args'],
                **params['kwargs']
            )
            Log.info(body)
        except Exception as e:
            Log.info('MarketingSmsConsumer failed: {}'.format(repr(e)))


class MarketingSmsProcess(object):
    """
    create by ld_wu 2018-04-24
    """

    days = 2
    active = 10
    suspend = 20
    potential_count = 80
    max_msg_count = 3

    @classmethod
    def send_demand_message(cls, demand_id):
        with DB() as session:
            demand = model_factory.demand.get_by_id(session, demand_id)
            if not demand:
                return

            area = model_factory.area.get_by_id(session, demand.area_id)

            senders = cls.get_senders(session, area.pid)
            to_send_list = cls.gen_message(session, senders, demand)
            cls.multi_send_msg(to_send_list)
            cls.update_sender(session, area.pid)

    @classmethod
    def potential_demand_message(cls, demand_id):
        """
        create by ld_wu 2018-06-28
        给潜在发送订单短信（80条）
        文档请看：https://shimo.im/docs/fMFAXUaaOjYerMYv/
        :param demand_id:
        :return:
        """
        with DB() as session:
            demand = model_factory.demand.get_by_id(session, demand_id)
            if not demand:
                return
            area = model_factory.area.get_by_id(session, demand.area_id)
            senders = cls.get_potential_senders(session, area.pid)
            to_send_list = cls.gen_message(
                session, senders, demand, use_virtual=False
            )
            cls.multi_send_msg(to_send_list)
            cls.update_potential_senders(senders)

    @classmethod
    def get_potential_senders(cls, session, area_id):
        """
        create by ld_wu 2018-06-28
        :param session:
        :param area_id:
        :return:
        """

        # 发送条数少于4次且首次发送日期在7天内的
        # 同时关注项目区域省份
        user_marketing_info = model_factory.user_marketing_info_july
        to_send_users = session.query(
            user_marketing_info
        ).filter(
            user_marketing_info.area_id == area_id,
            user_marketing_info.status == cls.active,
            user_marketing_info.total_send_count < cls.max_msg_count
        ).order_by(
            user_marketing_info.id.desc()
        ).limit(
            cls.potential_count
        ).all()
        return to_send_users

    @classmethod
    def update_potential_senders(cls, senders):
        today = date.today()
        for sender in senders:
            if not sender.first_send_date:
                sender.first_send_date = today
            sender.current_send_date = today
            sender.total_send_count += 1
            if sender.total_send_count >= cls.max_msg_count:
                sender.status = cls.suspend

    @classmethod
    def get_senders(cls, session, area_id):
        """
        create_by ld_wu 2018-04-24
        获取发送的用户列表
        """
        today = date.today()
        start_day = today - timedelta(days=cls.days)

        # 首次发送日期是三天内或者是首次发送的人
        # 同时关注项目区域
        to_send_users = session.query(model_factory.user_marketing_info).filter(
            or_(
                model_factory.user_marketing_info.first_send_date.is_(None),
                model_factory.user_marketing_info.first_send_date.between(
                    start_day, today
                )
            ),
            model_factory.user_marketing_info.area_id == area_id,
            model_factory.user_marketing_info.status == cls.active,
            model_factory.user_marketing_info.push_date != today
        ).all()
        return to_send_users

    @classmethod
    def update_sender(cls, session, area_id):
        """
        create by ld_wu 2018-04-24
        更新发送短信后的状态
        1、最近发送日期
        2、首次发送日期
        3、发送次数
        :return:
        """

        today = date.today()
        start_day = today - timedelta(days=cls.days)

        # 再次发送
        re_send_status_sql = """
            update 
                user_marketing_info
            set
                current_send_date = '{today}',
                total_send_count = total_send_count + 1
            where
                 first_send_date between '{start_day}' and '{today}'
                 and area_id = {area_id}
                 and status = {active}
                 and push_date != '{today}'
        """.format(
            today=today,
            start_day=start_day,
            area_id=area_id,
            active=cls.active
        )

        # 首次发送
        first_send_status_sql = """
            update 
                user_marketing_info
            set
                current_send_date = '{today}',
                first_send_date = '{today}',
                total_send_count = total_send_count + 1
            where
                 first_send_date is null
                 and area_id = {area_id}
                 and status = {active}
                 and push_date != '{today}'
        """.format(
            today=today,
            area_id=area_id,
            active=cls.active
        )

        # NOTE 执行顺序不可变
        session.execute(re_send_status_sql)
        session.execute(first_send_status_sql)

    @classmethod
    def gen_message(cls, session, senders, demand, use_virtual=False):
        """
        create by ld_wu 2018-04-24
        生成短信内容
        :return:
        """
        content = '在线派单！{city}{owner_name}刚刚发布了需求：{cate}{size}{span}{surr}LED屏，订单已独家分派给您，请尽快前往%s 联系他！--百度独家战略合作平台'

        # 修改拉新新项目消息的安装为采购安装，其余暂时不变
        cate_display = (
            '采购安装'
            if demand.cate == DemandCate.INSTALL.value
            else demand.cate_display
        )
        owner = model_factory.user.get_by_id(session, demand.uid)
        owner_name = (owner and owner.nickname) or '客户'
        content = content.format(
            size=demand.size_display,
            city=demand.city,
            cate=cate_display,
            span=demand.span_display,
            surr=demand.surr_display,
            owner_name=owner_name
        )

        to_send_list = list()
        mobile_mapping = dict()
        if use_virtual:
            mobile_list = [each.mobile for each in senders]
            mobile_mapping = cls.bind_number(session, mobile_list)

        for sender in senders:
            # TODO by ld_wu 2018-04-25 url包含被发送人的id, 发送内容待定
            host = os.environ.get('opc_host') or ''

            demand_url = urllib.parse.urljoin(
                host,
                '/api/d/{d_id}-{u_id}'.format(
                    d_id=format(demand.id, '02x'),  # 转成16进制
                    u_id=format(sender.user_id, '02x')  # 转成16进制
                )
            )
            tmp_element = {
                'mobile': mobile_mapping.get(sender.mobile, sender.mobile),
                'content': content % demand_url
            }
            to_send_list.append(tmp_element)
        return to_send_list

    @classmethod
    def multi_send_msg(cls, to_send_list):
        multi_sms_producer = Producer(MULTI_SMS_QUEUE)
        multi_sms_producer.send(json.dumps(to_send_list))

    @classmethod
    def bind_number(cls, session, mobile_list):
        """
        create by ld_wu 2017-10-25
        绑定短信虚拟号
        :return:
        """
        bind_sql = """
            insert into virtual_numbers 
                (tel_a, tel_x, for_sms, created_at, updated_at) 
            values
                {inset_values}
        """

        get_numbers_sql = """
            select 
                tel 
            from 
                number_pool_for_lt 
            where 
                reserved=false
            order by updated_at asc
        limit {limit};
        """.format(
            limit=cls.potential_count
        )

        fresh_numbers_updated_at_sql = """
            update 
                number_pool_for_lt
            set 
                updated_at = UNIX_TIMESTAMP()
            where 
                tel in {tels};
        """
        virtual_mobiles = list()
        if mobile_list:
            mobile_length = len(mobile_list)
            virtual_mobiles = session.execute(
                get_numbers_sql
            ).fetchall()
            virtual_mobiles = [i.tel for i in virtual_mobiles]

            # 更新 number_pool_for_lt 的 updated_at
            tmp_text = list(
                map(lambda x: '\'{}\''.format(x), virtual_mobiles)
            )
            virtual_mobiles_text = '({})'.format(', '.join(tmp_text))
            session.execute(
                fresh_numbers_updated_at_sql.format(
                    tels=virtual_mobiles_text,
                )
            )

            # 洗牌，打乱虚拟号顺序
            random.shuffle(virtual_mobiles)
            virtual_mobiles = virtual_mobiles[:mobile_length]
            timestamp_list = [int(time.time())] * mobile_length
            values = list(zip(
                mobile_list,
                virtual_mobiles,
                [1] * mobile_length,
                timestamp_list,
                timestamp_list,
            ))
            inset_values = map(
                lambda x: '(\'{}\', \'{}\', {}, {}, {})'.format(*x), values
            )
            if values:
                value_text = ', '.join(inset_values)
                session.execute(
                    bind_sql.format(inset_values=value_text)
                )
        return dict(zip(mobile_list, virtual_mobiles))


if __name__ == '__main__':
    pass
